﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Web;
using System.Linq;
using FastData.Caching;
using System.Collections;
using System.Web.Caching;

namespace FastData
{
    public abstract class QueryBase
    {
        internal List<DbParameter> Parameters = new List<DbParameter>();
        internal List<TableDependency> CacheDependencies = new List<TableDependency>();
        internal List<DbParameter> Fields = new List<DbParameter>();
        internal List<JoinInfo> Joins = new List<JoinInfo>();
        internal DataAccess Data;
        internal CommandType CommandType = CommandType.Text;
        internal TimeSpan SlidingExpiration = Cache.NoSlidingExpiration;
        internal DateTime AbsoluteExpiration = Cache.NoAbsoluteExpiration;
        internal int TakeCount;
        internal int SkipCount;
        internal string OrderByText;
        internal string WhereText;
        internal string FieldNames;
        internal string TableName;
        internal string IdName;
        internal bool IsDistinct;

        internal abstract string GetCommandText(ExecuteType type);

        internal QueryBase(DataAccess data)
        {
            Data = data;
        }

        internal void SetOrderBy(string field, SortDirection sortDirection = SortDirection.NotSet)
        {
            if (string.IsNullOrEmpty(OrderByText))
            {
                OrderByText = "ORDER BY ";
            }
            else
            {
                OrderByText += ",";
            }
            OrderByText += Data.Provider.GetDbObjectName(field) + " " + (sortDirection == SortDirection.NotSet ? "" : sortDirection.ToString());
        }

        internal void SetWhere(string whereText)
        {
            if (!string.IsNullOrEmpty(whereText))
            {
                WhereText += (string.IsNullOrEmpty(WhereText) ? "Where (" : " AND (") + whereText + ")";
            }
        }

        internal void SetWhere(string fieldName, object value)
        {
            SetWhere(fieldName, FilterType.Equal, value);
        }

        internal void SetWhere(FilterField[] filters)
        {
            SetWhere(string.Join(" AND ", filters.Select(f => GetFilterString(f.DataField, f.FilterType, f.Value))));
        }

        internal void SetWhere(string fieldName, FilterType filterType, object value)
        {
            SetWhere(GetFilterString(fieldName, filterType, value));
        }

        private string GetFilterString(string fieldName, FilterType filterType, object value)
        {
            string dataField = Data.Provider.GetDbObjectName(fieldName);
            fieldName = "FastDataField" + Parameters.Count + fieldName;
            string parameterName = Data.Provider.GetDbParameterName(fieldName);

            switch (filterType)
            {
                case FilterType.Equal:
                    AddParameter(fieldName, value);
                    return dataField + "=" + parameterName;

                case FilterType.EndsWith:
                    AddParameter(fieldName, "%" + value);
                    return dataField + " Like " + parameterName;

                case FilterType.StartsWith:
                    AddParameter(fieldName, value + "%");
                    return dataField + " Like " + parameterName;

                case FilterType.Contains:
                    AddParameter(fieldName, "%" + value + "%");
                    return dataField + " Like " + parameterName;

                case FilterType.Greater:
                    AddParameter(fieldName, value);
                    return dataField + ">" + parameterName;

                case FilterType.GreaterOrEqual:
                    AddParameter(fieldName, value);
                    return dataField + ">=" + parameterName;

                case FilterType.Less:
                    AddParameter(fieldName, value);
                    return dataField + "<" + parameterName;

                case FilterType.LessOrEqual:
                    AddParameter(fieldName, value);
                    return dataField + "<=" + parameterName;

                case FilterType.IsNull:
                    return dataField + " is null";

                case FilterType.NotIsNull:
                    return " NOT (" + dataField + " is null)";

                case FilterType.NotEqual:
                    AddParameter(fieldName, value);
                    return dataField + "<>" + parameterName;

                case FilterType.In:
                    List<string> parameterNames = new List<string>();

                    object[] values = (object[])value;
                    for (int i = 0; i < values.Length; i++)
                    {
                        parameterNames.Add(Data.Provider.GetDbParameterName(fieldName + i));
                        AddParameter(fieldName + i, values[i]);
                    }

                    return dataField + " IN (" + string.Join(",", parameterNames) + ")";

                default:
                    return "";
            }
        }

        internal DbParameter AddParameter(string name, object value)
        {
            return AddParameter(name, value, false);
        }

        internal DbParameter AddParameter(string name, object value, bool antiXSS)
        {
            DbParameter param = Data.Provider.CreateParameter(name, value, antiXSS);
            Parameters.Add(param);
            return param;
        }

        internal void AddParameter(DbParameter parameter)
        {
            Parameters.Add(parameter);
        }

        internal string Param(object value)
        {
            string name = "FastDataParameter" + Parameters.Count;
            AddParameter(name, value);
            return Data.Provider.GetDbParameterName(name);
        }
        
        internal void AddField(string name, object value, bool antiXSS)
        {
            Fields.Add(AddParameter(name, value, antiXSS));
        }

        public override string ToString()
        {
            return ToString(ExecuteType.NoneQuery);
        }

        public string ToString(ExecuteType type)
        {
            return GetCommandText(type);
        }
        
        internal string CacheKey
        {
            get
            {
                string key = ToString();
                foreach (DbParameter param in Parameters)
                {
                    key += param.ParameterName + param.Value;
                }
                return key;
            }
        }

        internal virtual void ExecuteCompleted()
        {
        }
    }

}
